package com.googlecode.adaboost.module;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.googlecode.adaboost.exception.AdaBoostException;
import com.googlecode.adaboost.trainer.AdaBoostConfiguration;

public class ModuleLoader {
	private AdaBoostConfiguration sysSettings;
	private Properties props;
	private HashMap<String, Constructor<Module>> constructors;

	public ModuleLoader(AdaBoostConfiguration settings) {
		this.sysSettings = settings;
		this.props = settings.getProperties();
		constructors = new HashMap<String, Constructor<Module>>();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Module loadModule(String name, Class requiredInterface)
			throws AdaBoostException {
		try {
			String prefix = "module." + name;
			String className = props.getProperty(prefix);
			if (className == null)
				throw new ClassNotFoundException(
						"No classname given in properties file for module."
								+ name);
			Constructor<Module> c = constructors.get(className);

			if (c == null) {
				Class<Module> mc = (Class<Module>) (Class.forName(className));
				if (requiredInterface.isAssignableFrom(mc) == false)
					throw new AdaBoostException("Class " + mc.getName()
							+ " is not an instance of "
							+ requiredInterface.getName());

				c = mc.getConstructor(new Class[0]);
				constructors.put(className, c);
			}

			Module m = (Module) c.newInstance(new Object[0]);

			Map<String, String> modulesSettings = PropertiesUtil.collectSubset(
					props, prefix + ".");

			m.initalize(sysSettings, modulesSettings);

			return m;
		} catch (Exception e) {
			throw new AdaBoostException("Couldn't load module '" + name + "'.",
					e);
		}
	}

	@SuppressWarnings({ "rawtypes" })
	public Module loadOptionalModule(String name, Class requiredInterface)
			throws AdaBoostException {
		String prefix = "module." + name;
		String className = props.getProperty(prefix);
		if (className == null)
			return null;
		return loadModule(name, requiredInterface);
	}
}
